In a large scale computer system, such as a database management system (DBMS), it is important to be able to support a number of different users concurrently. Without such a capability, the system would be little more than a standalone computer. To implement multi-user support, several different processing models have been utilized. One model that has been used is the multi-processing model. In multi-processing, each time a new user requests access to the system, a separate process is started. This process is in essence a separate execution of the software. Once started, the process services all of the requests from the user that spawned it. Under the multi-processing model, each process has its own separate memory space for use in storing and processing data.
Multi-processing is effective for supporting multiple users concurrently; however, it has severe scalability limitations. This is due mainly to two factors. First, spawning and maintaining a process involves a significant amount of overhead. Because of the high cost, only a small number of processes can be maintained at any one time. Second, the same set of data, used by multiple processes, may be stored redundantly; once in each process' memory space. This redundancy can waste a significant amount of system resources.
To overcome some of the limitations of multi-processing, the multi-thread model was developed. According to the multi-thread model, there is only one execution of the software. That is, only one process is spawned. From this one process, multiple threads can be spawned to perform the work necessary to service user requests.
Multi-threading has several advantages over multi-processing. First, because only one process is spawned, overhead is kept to a minimum. It is true that each thread carries with it some overhead cost, but this cost is negligible when compared with the cost of maintaining an entire process. Because multi-threading significantly reduces system overhead, many more users can be supported. Another advantage of multi-threading is that it minimizes the redundant storage of data. Because all of the threads are part of the same process, all of the threads can share the same memory space. This in turn makes it easier to implement a shared cache.
With a shared cache, it is only necessary to store a set of data once. After the data is cached, all of the threads can access it. By reducing redundant storage of data, multi-threading makes more efficient use of system resources.
Implementing a shared cache gives rise to increased efficiencies. However, a shared cache is not without its disadvantages. One of the drawbacks of a shared cache is that it can be difficult to render a set of data in the cache visible to only one user (i.e. to make the entry “private” to the user). As noted above, once an entry is stored in the shared cache, that entry is accessible to all threads. In certain applications, it is important to be able to make a cache entry private to a single user. One application in which this ability is important is in on-line analytical processing (OLAP).
An OLAP system is typically used to provide decision support services, such as forecasting, financial modeling, and what-if analysis. In performing what if analysis, an OLAP system typically performs at least three operations: (1) it retrieves historical data from a databases; (2) it changes the data in accordance with a what-if scenario posed by the user; and (3) based on the changed data, it determines what other data is changed. What-if analysis is a powerful tool because it allows a user to forecast how a change in one area may affect another. For example, a user may use what-if analysis to predict how sales in a region may change if the sales force is increased by ten percent.
As noted above, one of the operations performed in a what-if analysis is to change the retrieved historical data. This change typically is not an actual change but a proposed “what-if” change. Because it is not an actual change to the data in the database, only the user making the change should see it. All other users should still see the original data. In such a situation, there is a need to make the proposed change private to the user making the change. In a typical shared cache, however, the only mechanism for making a cache entry private is to employ locks, which typically require additional overhead and can result in deadlocking between threads competing for the resource. Hence, there exists a need for a mechanism that can support private data without hindering performance.